/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include <string.h>
#include <jni.h>
#include <android/log.h>
#include "cv.h"
#include "cxcore.h"
#include "bmpfmt.h"
#define ANDROID_LOG_VERBOSE ANDROID_LOG_DEBUG
#define LOG_TAG "CVJNI"
#define LOGV(...) __android_log_print(ANDROID_LOG_SILENT, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#ifdef __cplusplus
extern "C" {
#endif
IplImage* pImage = NULL;
CvHaarClassifierCascade* cascade = 0;
const char* cascade_name = "/sdcard/haarcascade_frontalface_alt2.xml";
int completed = 0;
IplImage* loadPixels(int* pixels, int width, int height);
IplImage* getIplImageFromIntArray(JNIEnv* env, jintArray array_data,
		jint width, jint height);

JNIEXPORT jboolean JNICALL Java_com_wypesoft_android_OpenCV_setSourceImage(
		JNIEnv* env, jobject thiz, jintArray photo_data, jint width,
		jint height) {
	if (pImage != NULL) {
		cvReleaseImage(&pImage);
		pImage = NULL;
	}
	pImage = getIplImageFromIntArray(env, photo_data, width, height);
	if (pImage == NULL) {
		return 0;
	}
	LOGI("Load Image Done.");
	return 1;
}
JNIEXPORT jbooleanArray JNICALL Java_com_wypesoft_android_OpenCV_getSourceImage(
		JNIEnv* env, jobject thiz) {
	if (pImage == NULL) {
		LOGE("No source image.");
		return 0;
	}
	cvFlip(pImage);
	int width = pImage->width;
	int height = pImage->height;
	int rowStep = pImage->widthStep;
	int headerSize = 54;
	int imageSize = rowStep * height;
	int fileSize = headerSize + imageSize;
	unsigned char* image = new unsigned char[fileSize];
	struct bmpfile_header* fileHeader = (struct bmpfile_header*) (image);
	fileHeader->magic[0] = 'B';
	fileHeader->magic[1] = 'M';
	fileHeader->filesz = fileSize;
	fileHeader->creator1 = 0;
	fileHeader->creator2 = 0;
	fileHeader->bmp_offset = 54;
	struct bmp_dib_v3_header_t* imageHeader =
			(struct bmp_dib_v3_header_t*) (image + 14);
	imageHeader->header_sz = 40;
	imageHeader->width = width;
	imageHeader->height = height;
	imageHeader->nplanes = 1;
	imageHeader->bitspp = 24;
	imageHeader->compress_type = 0;
	imageHeader->bmp_bytesz = imageSize;
	imageHeader->hres = 0;
	imageHeader->vres = 0;
	imageHeader->ncolors = 0;
	imageHeader->nimpcolors = 0;
	memcpy(image + 54, pImage->imageData, imageSize);
	jbooleanArray bytes = env->NewBooleanArray(fileSize);
	if (bytes == 0) {
		LOGE("Error in creating the image.");
		delete[] image;
		return 0;
	}
	env->SetBooleanArrayRegion(bytes, 0, fileSize, (jboolean*) image);
	delete[] image;
	return bytes;
}

IplImage* loadPixels(int* pixels, int width, int height) {
	int x, y;
	IplImage *img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
	unsigned char* base = (unsigned char*) (img->imageData);
	unsigned char* ptr;
	for (y = 0; y < height; y++) {
		ptr = base + y * img->widthStep;
		for (x = 0; x < width; x++) {
			// blue
			ptr[3 * x] = pixels[x + y * width] & 0xFF;
			// green
			ptr[3 * x + 1] = pixels[x + y * width] >> 8 & 0xFF;
			// blue
			ptr[3 * x + 2] = pixels[x + y * width] >> 16 & 0xFF;
		}
	}
	return img;
}
IplImage* getIplImageFromIntArray(JNIEnv* env, jintArray array_data,
		jint width, jint height) {
	int *pixels = env->GetIntArrayElements(array_data, 0);
	if (pixels == 0) {
		LOGE("Error getting int array of pixels.");
		return 0;
	}
	IplImage *image = loadPixels(pixels, width, height);
	env->ReleaseIntArrayElements(array_data, pixels, 0);
	if (image == 0) {
		LOGE("Error loading pixel array.");
		return 0;
	}
	return image;
}

JNIEXPORT int JNICALL Java_com_wypesoft_android_OpenCV_completedHaar(
		JNIEnv* env, jobject thiz) 
{	
	return completed;
}

JNIEXPORT int JNICALL Java_com_wypesoft_android_OpenCV_haar(
		JNIEnv* env, jobject thiz) 
{
    completed = 0;
    int scale = 1;

    // Creamos una imagen temporal para usarlas en el procesado
    IplImage* temp = cvCreateImage( cvSize(pImage->width/scale,pImage->height/scale), 8, 3 );

    // Creamos dos puntos para localizar el objeto
    CvPoint pt1, pt2;
    int i;

    IplImage* dummyImage = cvCreateImage(cvSize(1,1),IPL_DEPTH_8U, 1);
    cvErode(dummyImage, dummyImage);
    cvReleaseImage(&dummyImage);

    // Carga HaarClassifierCascade
    cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );
    
    if( !cascade )
    {
        LOGE("ERROR: No se encuentra el archivo de cascada\n");
    }

    CvMemStorage* storage = cvCreateMemStorage(0);

    CvSeq* faces;
    // Si se ha cargado el algoritmo de cascada lo ejecutamos:
    if( cascade )
    {

        // Puede haber mas de una cara en una imagen.
        // Detecta los objetos y los guarda en una secuencia que se recorrerá posteriormente
        faces = cvHaarDetectObjects( pImage, cascade, storage,
                                            1.1, 3, CV_HAAR_DO_CANNY_PRUNING,
                                            cvSize(20, 20) );
	
        // Recorre cada una de las caras encontradas.
        for( i = 0; i < (faces ? faces->total : 0); i++ )
        {
           // Crea un nuevo rectangulo para encuadrar la cara encontrada
            CvRect* r = (CvRect*)cvGetSeqElem( faces, i );

            // Encuentra las dimensiones de la cara y la escala si es necesario
            pt1.x = r->x*scale;
            pt2.x = (r->x+r->width)*scale;
            pt1.y = r->y*scale;
            pt2.y = (r->y+r->height)*scale;

            // Pinta el rectangulo en la imagen
            cvRectangle( pImage, pt1, pt2, CV_RGB(255,0,0), 2, 8, 0 );

        }
    }

    //Liberamos el espacio de la imagen temporal y de la memoria usada.
    cvReleaseImage( &temp );
    cvReleaseMemStorage(&storage);
    completed = 1;
	
    return faces->total; 
}


#ifdef __cplusplus
}
#endif

